﻿/*
#include <iostream>
#include <vector>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <string>
#include <locale>
#include <codecvt>
#include <sstream>
#include<iomanip>
#include <libxml/HTMLparser.h>
#include <libxml/xpath.h>
#include <regex>
#include <libxml/xmlstring.h>
#include <libxml/tree.h>
#include <fstream>

// 用于libcurl的写入回调函数
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) {
    ((std::string*)userp)->append((char*)contents, size * nmemb);
    return size * nmemb;
}

// 将UTF-8字符串转换为GBK字符串
std::string Utf8ToGbk(const std::string& utf8Str) {
    int nMultiByteLen = ::MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, NULL, 0);
    std::vector<wchar_t> wstrTo(nMultiByteLen, 0);
    ::MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, &wstrTo[0], nMultiByteLen);

    int len = ::WideCharToMultiByte(CP_ACP, 0, &wstrTo[0], -1, NULL, 0, NULL, NULL);
    std::vector<char> strTo(len, 0);
    ::WideCharToMultiByte(CP_ACP, 0, &wstrTo[0], -1, &strTo[0], len, NULL, NULL);

    return std::string(strTo.begin(), strTo.end());
}

std::string wide_to_utf8(const std::wstring& wstr) {
    std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
    return converter.to_bytes(wstr);
}

std::string url_encode(const std::string& value) {
    std::ostringstream escaped;
    escaped.fill('0');
    escaped << std::hex;

    for (auto i = value.begin(), n = value.end(); i != n; ++i) {
        if (isalnum((unsigned char)*i) || *i == '-' || *i == '_' || *i == '.' || *i == '~') {
            escaped << *i;
            continue;
        }

        if (*i == ' ') {
            escaped << '+';
            continue;
        }

        escaped << std::uppercase;
        escaped << '%' << std::setw(2) << int((unsigned char)*i);
        escaped << std::nouppercase;
    }

    return escaped.str();
}


int global_count = 1;  // 全局计数器，用于编号
int global_count2 = 0;
std::string info[100][3];

// 函数去除字符串中的空格和空字符
std::string removeSpaces(const std::string& input) {
    std::string result;
    for (char ch : input) {
        if (!isspace(static_cast<unsigned char>(ch))) {
            result += ch;
        }
    }
    return result;
}

void findAndPrintATags(xmlNode* node, std::vector<std::vector<std::string>>& musicList) {
    if (node->type == XML_ELEMENT_NODE && xmlStrcmp(node->name, (const xmlChar*)"a") == 0) {
        xmlChar* href = xmlGetProp(node, (xmlChar*)"href");
        if (href != nullptr) {
            xmlChar* musicPos = (xmlChar*)xmlStrstr(href, (xmlChar*)"/music/");
            if (musicPos != NULL) {
                musicPos += xmlStrlen((xmlChar*)"/music/"); // 移动到"/music/"之后的位置
                std::string musicId(reinterpret_cast<char*>(musicPos));

                std::string songName;
                std::string artistName;

                xmlNode* child = node->children;
                while (child != nullptr) {
                    if (child->type == XML_ELEMENT_NODE) {
                        if (xmlStrcmp(child->name, (const xmlChar*)"span") == 0) { 
                            xmlChar* spanText = xmlNodeGetContent(child);
                            if (spanText != nullptr) {
                                songName = reinterpret_cast<char*>(spanText);
                                xmlFree(spanText);
                            }
                        }
                        else if (xmlStrcmp(child->name, (const xmlChar*)"small") == 0) {
                            xmlChar* smallText = xmlNodeGetContent(child);
                            if (smallText != nullptr) {
                                artistName = reinterpret_cast<char*>(smallText);
                                xmlFree(smallText);
                            }
                        }
                    }
                    child = child->next;
                }

                // 只打印单数编号的条目
                if (global_count % 2 != 0) {
                    std::vector<std::string> musicInfo = { musicId, removeSpaces(Utf8ToGbk(songName)), removeSpaces(Utf8ToGbk(artistName)) };
                    musicList.push_back(musicInfo);
                }
                global_count++;  // 递增编号
            }
            xmlFree(href);
        }
    }

    for (xmlNode* child = node->children; child != nullptr; child = child->next) {
        findAndPrintATags(child, musicList);
    }
}


size_t write_data(void* ptr, size_t size, size_t nmemb, std::ofstream* stream) {
    if (stream == nullptr || !stream->is_open()) {
        std::cerr << "write_data: stream is null or not open" << std::endl;
        return 0;
    }
    stream->write(static_cast<char*>(ptr), size * nmemb);
    // 通常不检查stream->good()因为write()很少会失败，除非磁盘满或权限问题
    // 如果需要更严格的错误处理，可以在这里添加
    return size * nmemb;
}


//下载函数
void downloadSong(const char* url);
void downloadSong(const char* url) {
    CURL* curl;
    CURLcode res;

    // 初始化libcurl
    curl_global_init(CURL_GLOBAL_ALL);

    // 初始化CURL句柄
    curl = curl_easy_init();
    if (curl) {

        // 构造保存文件的路径（与源代码文件同级或指定路径）
        std::string outputFilename = "qt.mp3";

        // 打开文件以二进制模式写入数据
        std::ofstream file(outputFilename, std::ios::binary);
        if (!file.is_open()) {
            std::cerr << "Unable to open file for writing: " << outputFilename << std::endl;
            curl_easy_cleanup(curl);
            curl_global_cleanup();

        }

        // 设置libcurl的写入回调函数和文件流
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &file);

        // 设置要访问的URL
        curl_easy_setopt(curl, CURLOPT_URL, url);

        // （可选）设置其他CURL选项，如超时、代理等

        // 执行请求
        res = curl_easy_perform(curl);

        // 检查请求是否成功
        if (res != CURLE_OK) {
            std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
            // 如果需要，可以在这里添加清理已写入数据的文件的代码
        }

        // 关闭文件流（注意：在C++17及更高版本中，std::ofstream的析构函数会自动关闭文件）
        file.close(); // 但在早期版本中或显式关闭是一个好习惯

        // 清理CURL句柄
        curl_easy_cleanup(curl);
    }

    // 清理libcurl
    curl_global_cleanup();
}

//下载操作函数
void down(int num);
void down(int num) {

}

std::vector<std::vector<std::string>> printMenu();
std::vector<std::vector<std::string>> printMenu(){
    //输入查找数据
    CURL* curl;
    CURLcode res;
    std::string readBuffer;

    curl = curl_easy_init();
    if (curl) {
        std::locale::global(std::locale(""));

        // 设置 wcin 和 wcout 的 locale
        std::wcin.imbue(std::locale());
        std::wcout.imbue(std::locale());

        std::wstring input;
        std::wcout << L"请输入中文字符: ";
        std::getline(std::wcin, input);

        std::wstring wstr = input;
        std::string utf8_str = wide_to_utf8(wstr);
        std::string encoded_str = url_encode(utf8_str);
        std::string link1 = "https://www.gequbao.com/s/";
        std::string link2 = link1.append(encoded_str);
        std::cout << link2;

        curl_easy_setopt(curl, CURLOPT_URL, link2.c_str()); // 替换为实际的URL
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);

        // 执行HTTP GET请求
        res = curl_easy_perform(curl);

        // 检查错误
        if (res != CURLE_OK) {
            std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
        }
        else {
            // 假设HTML内容是UTF-8编码的，将其转换为GBK并打印
            std::string gbkContent = Utf8ToGbk(readBuffer);
            std::string initContent = readBuffer;
            //***********************************************


            xmlChar* htmlContent = (xmlChar*)readBuffer.c_str();

            // 解析HTML字符串
            xmlDoc* doc = htmlReadDoc(htmlContent, NULL, NULL, HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
            if (doc == nullptr) {
                std::cerr << "Error loading HTML document" << std::endl;
                
            }

            // 获取文档的根节点
            xmlNode* rootElement = xmlDocGetRootElement(doc);
            if (rootElement == nullptr) {
                std::cerr << "Error: no root element" << std::endl;
                xmlFreeDoc(doc);
                
            }

            // 遍历文档树并打印所有<a>标签的href属性
            std::vector<std::vector<std::string>> music;
            findAndPrintATags(rootElement, music);
            

            return music;
            // 清理
            xmlFreeDoc(doc);
            xmlCleanupParser();

        }

        // 清理
        curl_easy_cleanup(curl);
        }
    }
    
 

int main() {
    // 调用 printMenu 函数并接收返回的二维数组
    std::vector<std::vector<std::string>> musicList = printMenu();

    // 添加编号声明
    int num = 0;

    // 遍历二维数组并打印每个子数组的内容
    for (const auto& music : musicList) {
        std::cout << "编号: " << num++ << " - ";
        if (music.size() >= 3) {  // 确保子数组有足够的元素
            std::cout<< "Music ID: " << music[0] << ", 歌名: " << music[1] << ", 歌手: " << music[2] << std::endl;
            
        }
        else {
            std::cout << "数据不完整" << std::endl;
        }
    }


    std::cout << "请输入你要下载音乐的序号:\n";
    int opt;
    std::cin >> opt;

    // 将用户输入的序号转换为字符串
    std::string optStr = musicList[opt][0];

    
    // 传递musicId给解析函数，获取实际下载链接
    //std::cout << std::stoi(optStr) << std::endl;
    

    return 0;
}
*/



#include <iostream>  
#include <string>  
#include <vector>  
#include <curl/curl.h>  
#include <windows.h>  
#include <iomanip>
#include <sstream>
#include <libxml/HTMLparser.h>
#include <regex>
#include <nlohmann/json.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale>
#include <codecvt>
#include <libxml/xpath.h>
#include <libxml/xmlstring.h>
#include <libxml/tree.h>
#include <fstream>

//第一大部分,转码与打印
// 将UTF-8字符串转换为GBK字符串
std::string Utf8ToGbk(const std::string& utf8Str) {
    int nMultiByteLen = ::MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, NULL, 0);
    std::vector<wchar_t> wstrTo(nMultiByteLen, 0);
    ::MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, &wstrTo[0], nMultiByteLen);

    int len = ::WideCharToMultiByte(CP_ACP, 0, &wstrTo[0], -1, NULL, 0, NULL, NULL);
    std::vector<char> strTo(len, 0);
    ::WideCharToMultiByte(CP_ACP, 0, &wstrTo[0], -1, &strTo[0], len, NULL, NULL);

    return std::string(strTo.begin(), strTo.end());
}

// 用于libcurl的写入回调函数  
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) {
    ((std::string*)userp)->append((char*)contents, size * nmemb);
    return size * nmemb;
}

std::string wide_to_utf8(const std::wstring& wstr) {
    std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
    return converter.to_bytes(wstr);
}

std::string url_encode(const std::string& value) {
    std::ostringstream escaped;
    escaped.fill('0');
    escaped << std::hex;

    for (auto i = value.begin(), n = value.end(); i != n; ++i) {
        if (isalnum((unsigned char)*i) || *i == '-' || *i == '_' || *i == '.' || *i == '~') {
            escaped << *i;
            continue;
        }

        if (*i == ' ') {
            escaped << '+';
            continue;
        }

        escaped << std::uppercase;
        escaped << '%' << std::setw(2) << int((unsigned char)*i);
        escaped << std::nouppercase;
    }

    return escaped.str();
}


int global_count = 1;  // 全局计数器，用于编号
int global_count2 = 0;
std::string info[100][3];

// 函数去除字符串中的空格和空字符
std::string removeSpaces(const std::string& input) {
    std::string result;
    for (char ch : input) {
        if (!isspace(static_cast<unsigned char>(ch))) {
            result += ch;
        }
    }
    return result;
}

void findAndPrintATags(xmlNode* node, std::vector<std::vector<std::string>>& musicList) {
    if (node->type == XML_ELEMENT_NODE && xmlStrcmp(node->name, (const xmlChar*)"a") == 0) {
        xmlChar* href = xmlGetProp(node, (xmlChar*)"href");
        if (href != nullptr) {
            xmlChar* musicPos = (xmlChar*)xmlStrstr(href, (xmlChar*)"/music/");
            if (musicPos != NULL) {
                musicPos += xmlStrlen((xmlChar*)"/music/"); // 移动到"/music/"之后的位置
                std::string musicId(reinterpret_cast<char*>(musicPos));

                std::string songName;
                std::string artistName;

                xmlNode* child = node->children;
                while (child != nullptr) {
                    if (child->type == XML_ELEMENT_NODE) {
                        if (xmlStrcmp(child->name, (const xmlChar*)"span") == 0) { 
                            xmlChar* spanText = xmlNodeGetContent(child);
                            if (spanText != nullptr) {
                                songName = reinterpret_cast<char*>(spanText);
                                xmlFree(spanText);
                            }
                        }
                        else if (xmlStrcmp(child->name, (const xmlChar*)"small") == 0) {
                            xmlChar* smallText = xmlNodeGetContent(child);
                            if (smallText != nullptr) {
                                artistName = reinterpret_cast<char*>(smallText);
                                xmlFree(smallText);
                            }
                        }
                    }
                    child = child->next;
                }

                // 只打印单数编号的条目
                if (global_count % 2 != 0) {
                    std::vector<std::string> musicInfo = { musicId, removeSpaces(Utf8ToGbk(songName)), removeSpaces(Utf8ToGbk(artistName)) };
                    musicList.push_back(musicInfo);
                }
                global_count++;  // 递增编号
            }
            xmlFree(href);
        }
    }

    for (xmlNode* child = node->children; child != nullptr; child = child->next) {
        findAndPrintATags(child, musicList);
    }
}

std::vector<std::vector<std::string>> printMenu();
std::vector<std::vector<std::string>> printMenu() {
    //输入查找数据
    CURL* curl;
    CURLcode res;
    std::string readBuffer;

    curl = curl_easy_init();
    if (curl) {
        std::locale::global(std::locale(""));

        // 设置 wcin 和 wcout 的 locale
        std::wcin.imbue(std::locale());
        std::wcout.imbue(std::locale());

        std::wstring input;
        std::wcout << L"请输入中文字符: ";
        std::getline(std::wcin, input);

        std::wstring wstr = input;
        std::string utf8_str = wide_to_utf8(wstr);
        std::string encoded_str = url_encode(utf8_str);
        std::string link1 = "https://www.gequbao.com/s/";
        std::string link2 = link1.append(encoded_str);
        std::cout << link2;

        curl_easy_setopt(curl, CURLOPT_URL, link2.c_str()); // 替换为实际的URL
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);

        // 执行HTTP GET请求
        res = curl_easy_perform(curl);

        // 检查错误
        if (res != CURLE_OK) {
            std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
        }
        else {
            // 假设HTML内容是UTF-8编码的，将其转换为GBK并打印
            std::string gbkContent = Utf8ToGbk(readBuffer);
            std::string initContent = readBuffer;
            //***********************************************


            xmlChar* htmlContent = (xmlChar*)readBuffer.c_str();

            // 解析HTML字符串
            xmlDoc* doc = htmlReadDoc(htmlContent, NULL, NULL, HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
            if (doc == nullptr) {
                std::cerr << "Error loading HTML document" << std::endl;

            }

            // 获取文档的根节点
            xmlNode* rootElement = xmlDocGetRootElement(doc);
            if (rootElement == nullptr) {
                std::cerr << "Error: no root element" << std::endl;
                xmlFreeDoc(doc);

            }

            // 遍历文档树并打印所有<a>标签的href属性
            std::vector<std::vector<std::string>> music;
            findAndPrintATags(rootElement, music);


            return music;
            // 清理
            xmlFreeDoc(doc);
            xmlCleanupParser();

        }

        // 清理
        curl_easy_cleanup(curl);
    }
}


//第一部分 将收到的数字即音乐序号拼接url，获取整体html


std::string getHtml(std::string musicId);
std::string getHtml(std::string musicId) {
    CURL* curl;
    CURLcode res;
    std::string readBuffer;

    curl = curl_easy_init();
    if (curl) {
       
        std::string url = "https://www.gequbao.com/music/"+ musicId;
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // 替换为实际的URL  
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);

        // 执行HTTP GET请求  
        res = curl_easy_perform(curl);

        // 检查错误  
        if (res != CURLE_OK) {
            std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
        }
        else {
            // 假设HTML内容是UTF-8编码的，将其转换为GBK并打印  
            std::string gbkContent = Utf8ToGbk(readBuffer);
            return gbkContent;
            
        }

        // 清理  
        curl_easy_cleanup(curl);
    }
}

//第二部分 提取javascript的window.play_id的value
// 函数用于从HTML中提取<script>标签的内容
std::string extractScript(xmlNode* node) {
    xmlChar* content = xmlNodeGetContent(node);
    std::string scriptContent = (content != NULL) ? std::string((char*)content) : "";
    xmlFree(content);
    return scriptContent;
}

// 递归函数用于查找所有的<script>标签
void findScriptTags(xmlNode* node, std::vector<std::string>& scripts) {
    if (node == nullptr) return;

    // 检查当前节点是否是<script>标签
    if (node->type == XML_ELEMENT_NODE && strcmp((const char*)node->name, "script") == 0) {
        std::string script = extractScript(node);
        scripts.push_back(script);
    }

    // 递归遍历子节点
    for (xmlNode* child = node->children; child != nullptr; child = child->next) {
        findScriptTags(child, scripts);
    }
}

//调用第二部分的函数进行解析获取
std::string getPlayId(std::string html);
std::string getPlayId(std::string html) {

    // 解析HTML文档
    xmlDoc* doc = htmlReadDoc((xmlChar*)html.c_str(), NULL, NULL, HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
    if (doc == nullptr) {
        std::cerr << "Failed to parse HTML\n";
        
    }

    xmlNode* root_element = xmlDocGetRootElement(doc);

    std::vector<std::string> scripts;
    findScriptTags(root_element, scripts);

    // 遍历所有<script>标签
    for (const auto& script : scripts) {
        // 使用正则表达式查找window.play_id
        std::regex playIdRegex("window\\.play_id\\s*=\\s*'([^']*)'");
        std::smatch match;
        if (std::regex_search(script, match, playIdRegex) && match.size() > 1) {
            std::cout << "Found play_id: " << match[1].str() << std::endl;
            return match[1].str();
        }
    }

    // 清理
    xmlFreeDoc(doc);
    xmlCleanupParser();
}

//第三部分 根据获取的play_id发送给/api/play-url,并以json格式发送id:音乐id即可获取json，并从中解析url
std::string getDownloadUrl(std::string playId);
std::string getDownloadUrl(std::string playId) {
    CURL* curl;
    CURLcode res;
    std::string readBuffer; // 用于存储响应数据

    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();

    if (curl) {
        struct curl_slist* headers = NULL;
        headers = curl_slist_append(headers, "Content-Type: application/json");
        headers = curl_slist_append(headers, "Accept: application/json");

        // 设置User-Agent为requests库的默认值
        std::string requests_user_agent = "python-requests/2.25.1 CPython/3.8.5 Linux/5.4.0-42-generic";
        headers = curl_slist_append(headers, ("User-Agent: " + requests_user_agent).c_str());

        // 目标URL
        std::string url = "https://www.gequbao.com/api/play-url"; // 确保URL是正确的

        // 要发送的数据
        std::string jsonTemplate = R"({"id":"${id}"})";
        std::string json_data;

        // 替换模板中的占位符为实际的playId值
        size_t pos = jsonTemplate.find("${id}");
        if (pos != std::string::npos) {
            json_data = jsonTemplate.substr(0, pos) + playId + jsonTemplate.substr(pos + std::string("${id}").length());
        }
        else {
            // 如果没有找到占位符，直接使用模板字符串
            json_data = jsonTemplate;
        }

        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_data.c_str());
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // 自动跟随重定向

        res = curl_easy_perform(curl);

        if (res != CURLE_OK) {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        }
        else {

            // 使用nlohmann/json解析JSON响应
            try {
                auto json_response = nlohmann::json::parse(readBuffer);
                //std::cout << "Parsed JSON: " << json_response.dump(4) << std::endl;

                // 获取URL
                std::string url = json_response["data"]["url"];
                
                return url;
            }
            catch (nlohmann::json::parse_error& e) {
                std::cerr << "JSON parse error: " << e.what() << '\n';
                std::cout << "Response Text: " << readBuffer << std::endl;
            }
        }

        curl_slist_free_all(headers); // 清理头部列表
        curl_easy_cleanup(curl);
    }

    curl_global_cleanup();
}

//下载函数回调支持
size_t write_data(void* ptr, size_t size, size_t nmemb, std::ofstream* stream) {
    if (stream == nullptr || !stream->is_open()) {
        std::cerr << "write_data: stream is null or not open" << std::endl;
        return 0;
    }
    stream->write(static_cast<char*>(ptr), size * nmemb);
    // 通常不检查stream->good()因为write()很少会失败，除非磁盘满或权限问题
    // 如果需要更严格的错误处理，可以在这里添加
    return size * nmemb;
}

//下载函数
void downloadSong(const char* url, std::string songName, std::string singer);
void downloadSong(const char* url, std::string songName, std::string singer) {
    CURL* curl;
    CURLcode res;

    // 初始化libcurl
    curl_global_init(CURL_GLOBAL_ALL);

    // 初始化CURL句柄
    curl = curl_easy_init();
    if (curl) {

        // 构造保存文件的路径（与源代码文件同级或指定路径）使用 std::stringstream 拼接字符串
        
        std::string tSongName=songName;
        std::string tSinger=singer;
        std::string fe = ".mp3";
        
        if (songName.empty() || singer.empty()) {
            std::cerr << "Song name or singer name is empty." << std::endl;
        }
        std::stringstream ss;
        ss << tSongName << "--" << tSinger << ".mp3";
        std::string outputFilename = ss.str();
        std::cout << outputFilename;
        // 打开文件以二进制模式写入数据
        std::ofstream file(outputFilename, std::ios::binary);
        if (!file.is_open()) {
            std::cerr << "Unable to open file for writing: " << outputFilename << std::endl;
            curl_easy_cleanup(curl);
            curl_global_cleanup();

        }
        else {
            std::cout << outputFilename;
        }

        // 设置libcurl的写入回调函数和文件流
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &file);

        // 设置要访问的URL
        curl_easy_setopt(curl, CURLOPT_URL, url);

        // （可选）设置其他CURL选项，如超时、代理等

        // 执行请求
        res = curl_easy_perform(curl);

        // 检查请求是否成功
        if (res != CURLE_OK) {
            std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
            // 如果需要，可以在这里添加清理已写入数据的文件的代码
        }

        // 关闭文件流（注意：在C++17及更高版本中，std::ofstream的析构函数会自动关闭文件）
        file.close(); // 但在早期版本中或显式关闭是一个好习惯

        // 清理CURL句柄
        curl_easy_cleanup(curl);
    }

    // 清理libcurl
    curl_global_cleanup();
}



int main() {

    //第一大部分，输入字符打印菜单,调用 printMenu 函数并接收返回的二维数组
    std::vector<std::vector<std::string>> musicList = printMenu();

    // 添加编号声明
    int num = 1;

    // 遍历二维数组并打印每个子数组的内容
    for (const auto& music : musicList) {
        std::cout << "编号: " << num++ << " - ";
        if (music.size() >= 3) {  // 确保子数组有足够的元素
            std::cout << "Music ID: " << music[0] << ", 歌名: " << music[1] << ", 歌手: " << music[2] << std::endl;

        }
        else {
            std::cout << "数据不完整" << std::endl;
        }
    }


    std::cout << "请输入你要下载音乐的序号:\n";
    int opt;
    std::cin >> opt;
    std::string id = musicList[opt-1][0];
    std::string songName = musicList[opt - 1][1];
    std::string singer = musicList[opt - 1][2];
    std::string htmlContent=getHtml(id);
    std::string playId = getPlayId(htmlContent);
    std::string downloadurl = getDownloadUrl(playId);
    downloadSong(downloadurl.c_str(), songName.c_str(), singer.c_str());
    return 0;
}


